package lambdasinaction.chap10.my;

import java.util.Optional;

/**
 * 人
 * 在代码中使用 Optional，能非常清晰地界定出变量值的缺失是结构上的问题，
 * 还是你算法上的缺陷，抑或是你数据上的问题。
 * 特别强调，引入 Optional 类的意图并非要消除每一个 null 引用。
 * 与此相反，它的目标是帮助你更好地设计出普适的 API
 * 让程序员看到方法签名，就能了解它是否接受一个 Optional 的值。
 * 这种强制会让你更积极地将变量从 Optional 中解包出来，直面缺失的变量值。
 *
 * @author Fish
 * @since 0.0.1 2018/8/10 19:51
 */
public class MyPerson {

  private MyCar car;

  /**
   * 人可能有车，也可能没车
   * 因此将这个字段声明为 Optional
   */
  private Optional<MyCar> optionalCar;

  public Optional<MyCar> getOptionalCar() {
    return optionalCar;
  }

  public void setOptionalCar(Optional<MyCar> optionalCar) {
    this.optionalCar = optionalCar;
  }

  public MyCar getCar() {
    return car;
  }

  public void setCar(MyCar car) {
    this.car = car;
  }

  /**
   * 这段代码看起来相当正常，但是现实生活中很多人没有车。 所以调用 getCar 方法的结果会怎样呢？ 在实践中，一种比较常见的做法是返回一个 null 引用， 表示该值的缺失，即用户没有车。
   * 而接下来，对 getInsurance 的调用会返回 null 引用的 insurance， 这会导致运行时出现一个 NullPointerException，终止程序的运行。
   * 但这还不是全部。 如果返回的 person 值为 null 会怎样？ 如果 getInsurance 的返回值也是 null，结果又会怎样？
   */
  public String getCarInsuranceName(MyPerson person) {
    return person.getCar().getInsurance().getName();
  }

  /**
   * null 安全的第一种尝试：深层质疑 每个 null 检查都会增加调用链上剩余代码的嵌套层数
   */
  public String getCatInsuranceName2(MyPerson person) {
    if (person != null) {
      MyCar car = person.getCar();
      if (car != null) {
        MyInsurance insurance = car.getInsurance();
        if (insurance != null) {
          return insurance.getName();
        }
      }
    }
    return "Unknown";
  }

  /**
   * null 安全的第二种尝试：过多的退出语句
   * 第二种尝试中，你试图避免深层地柜的 if 语句块，采用了一种不同的策略：
   * 每次你遭遇 null 变量，都返回一个字符串常量"Unknown"。
   * 然而，这种方式远非理想，现在这个方法有了四个截然不同的退出点，使得代码的维护异常艰难。
   * 更糟的是，发生 null 时返回的默认值，即字符串"Unknown"在三个不同的地方重复出现————出现拼写错误的概率不小！
   * 当然，你可能会说，我们可以用把它们抽取到一个常量中的方式避免这种问题。
   * 进一步而言，这种流程是极易出错的；
   * 如果你忘记检查了那个可能为 null 的属性会怎样？
   * 通过这一章的学习，你会了解使用 null 来表示变量值的缺失是大错特错的。
   * 你需要更优雅的方式来对缺失的变量值建模。
   * @param person
   * @return
   */
  public String getCarInsuranceName3(MyPerson person) {
    if (person == null) { // 每个 null 检查都会添加新的退出点
      return "Unknown";
    }
    MyCar car = person.getCar();
    if (car == null) {
      return "Unknown";
    }
    MyInsurance insurance = car.getInsurance();
    if (insurance == null) {
      return "Unknown";
    }
    return insurance.getName();
  }
}
